﻿using HtmlAgilityPack;
using Journeo_backend.Configuration;
using Microsoft.Extensions.Options;
using System.Net;

namespace Journeo_backend.Services.Implementations
{
    public class ScraperService : IScraperService
    {
        private readonly ApplicationDbContext _context;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILogger<ScraperService> _logger;
        private readonly SemaphoreSlim _semaphore = new(5); // 限制并发请求
        private readonly IProxyPool _proxyPool;
        private readonly IOptions<ProxyPoolOptions> _proxyOptions;

        public ScraperService(
        ApplicationDbContext context,
        IHttpClientFactory httpClientFactory,
        ILogger<ScraperService> logger,
        IProxyPool proxyPool,
        IOptions<ProxyPoolOptions> proxyOptions)
        {
            _context = context;
            _httpClientFactory = httpClientFactory;
            _logger = logger;
            _proxyPool = proxyPool;
            _proxyOptions = proxyOptions;
        }

        // 创建带代理的HttpClient
        private (HttpClient Client, string ProxyAddress) CreateHttpClientWithProxy()
        {
            var proxyConfig = _proxyPool.GetRandomProxy();
            var webProxy = proxyConfig as WebProxy;

            if (webProxy == null)
            {
                _logger.LogError("Failed to get valid proxy configuration");
                throw new InvalidOperationException("Invalid proxy configuration");
            }

            var handler = new HttpClientHandler
            {
                Proxy = webProxy,
                UseProxy = true,
                UseDefaultCredentials = false,
                PreAuthenticate = true,
                // 启用所有SSL/TLS版本
                SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
                ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
            };

            // 创建并配置 HttpClient
            var client = new HttpClient(handler)
            {
                Timeout = TimeSpan.FromSeconds(_proxyOptions.Value.TimeoutSeconds)
            };

            // 添加基本的请求头
            client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");

            var proxyAddress = webProxy.Address?.ToString();

            // 添加自定义标头来追踪代理地址
            if (!string.IsNullOrEmpty(proxyAddress))
            {
                client.DefaultRequestHeaders.Add("X-Used-Proxy", proxyAddress);
            }

            _logger.LogInformation($"Created HTTP client with proxy: {proxyAddress}");
            return (client, proxyAddress);
        }

        // 使用重试机制发起请求
        private async Task<string> MakeRequestWithRetryAsync(string url)
        {
            var retryCount = _proxyOptions.Value.RetryCount;
            var lastException = default(Exception);
            HttpClient client = null;
            string currentProxyAddress = null;

            for (int i = 0; i < retryCount; i++)
            {
                try
                {
                    (client, currentProxyAddress) = CreateHttpClientWithProxy();
                    _logger.LogInformation($"Attempting request with proxy: {currentProxyAddress}");

                    using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
                    // 添加额外的请求头
                    requestMessage.Headers.Add("Connection", "keep-alive");
                    requestMessage.Headers.Add("Pragma", "no-cache");
                    requestMessage.Headers.Add("Cache-Control", "no-cache");

                    var response = await client.SendAsync(requestMessage);

                    // 处理特定的错误状态码
                    if (response.StatusCode == System.Net.HttpStatusCode.ProxyAuthenticationRequired)
                    {
                        _logger.LogWarning($"Proxy authentication required for {currentProxyAddress}");
                        _proxyPool.MarkProxyAsFailed(currentProxyAddress);
                        continue;
                    }

                    response.EnsureSuccessStatusCode();
                    var content = await response.Content.ReadAsStringAsync();

                    // 成功获取内容，返回
                    return content;
                }
                catch (HttpRequestException ex) when (ex.Message.Contains("407"))
                {
                    _logger.LogWarning($"Proxy authentication failed for {currentProxyAddress}: {ex.Message}");
                    _proxyPool.MarkProxyAsFailed(currentProxyAddress);
                }
                catch (Exception ex)
                {
                    lastException = ex;
                    _logger.LogWarning($"Request failed (attempt {i + 1}/{retryCount}) with proxy {currentProxyAddress}: {ex.Message}");

                    // 标记当前代理为失败
                    if (!string.IsNullOrEmpty(currentProxyAddress))
                    {
                        try
                        {
                            _proxyPool.MarkProxyAsFailed(currentProxyAddress);
                        }
                        catch (Exception proxyEx)
                        {
                            _logger.LogError($"Error marking proxy as failed: {proxyEx.Message}");
                        }
                    }
                }
                finally
                {
                    client?.Dispose();
                }

                if (i < retryCount - 1)
                {
                    var delayMs = 1000 * Math.Pow(2, i); // 指数退避：1秒、2秒、4秒...
                    _logger.LogInformation($"Waiting {delayMs / 1000} seconds before next retry...");
                    await Task.Delay((int)delayMs);
                }
            }

            throw new HttpRequestException($"Request failed after {retryCount} attempts", lastException);
        }

        // 爬取所有数据
        public async Task ScrapeAllDataAsync()
        {
            // await ScrapeRegionsAsync();
            var cities = await _context.Region.Where(r => r.RegionLevel == "city").ToListAsync();

            foreach (var city in cities)
            {
                try
                {
                    await _semaphore.WaitAsync();
                    await ScrapeAttractionsForCityAsync(city.ExternalUrl, city.RegionId);
                }
                finally
                {
                    _semaphore.Release();
                }
            }
        }


        // 爬取地区数据
        public async Task ScrapeRegionsAsync()
        {
            try
            {
                var content = await MakeRequestWithRetryAsync("https://travel.qunar.com/place/");

                var doc = new HtmlDocument();
                doc.LoadHtml(content);

                // 处理其他省份
                var regionGroups = doc.DocumentNode.SelectNodes("//dl[@class='listbox']");
                foreach (var regionGroup in regionGroups)
                {
                    var provinces = regionGroup.SelectNodes(".//div[@class='titbox']/span[@class='tit']");
                    if (provinces != null)
                    {
                        foreach (var province in provinces)
                        {
                            var provinceName = province.InnerText.Replace("：", "").Trim();

                            // 删除string中的&nbsp; 如福&nbsp;&nbsp;&nbsp;&nbsp;建，改为福建
                            provinceName = provinceName.Replace("&nbsp;", "");

                            var provinceEntity = new Region
                            {
                                RegionName = provinceName,
                                RegionLevel = "province"
                            };
                            await _context.Region.AddAsync(provinceEntity);
                            await _context.SaveChangesAsync();

                            // 处理省份下的城市
                            var cities = province.ParentNode.SelectNodes("following-sibling::ul[1]//a");
                            if (cities != null)
                            {
                                foreach (var city in cities)
                                {
                                    var cityName = city.InnerText.Trim();
                                    var cityUrl = city.GetAttributeValue("href", "");
                                    var cityEntity = new Region
                                    {
                                        RegionName = cityName,
                                        RegionLevel = "city",
                                        ParentId = provinceEntity.RegionId,
                                        ExternalUrl = cityUrl
                                    };
                                    await _context.Region.AddAsync(cityEntity);
                                }
                            }
                            await _context.SaveChangesAsync();
                        }
                    }
                }

                // 处理直辖市
                var municipalities = doc.DocumentNode.SelectNodes("//dt[contains(text(),'直辖市')]/following-sibling::dd[1]//a");
                foreach (var municipality in municipalities)
                {
                    var name = municipality.InnerText.Trim();
                    var url = municipality.GetAttributeValue("href", "");

                    // 保存省级记录
                    var province = new Region
                    {
                        RegionName = name,
                        RegionLevel = "province",
                        ExternalUrl = url
                    };
                    await _context.Region.AddAsync(province);
                    await _context.SaveChangesAsync();

                    // 同时保存市级记录
                    var city = new Region
                    {
                        RegionName = name,
                        RegionLevel = "city",
                        ParentId = province.RegionId,
                        ExternalUrl = url
                    };
                    await _context.Region.AddAsync(city);
                    await _context.SaveChangesAsync();
                }
            }
            catch (HttpRequestException ex)
            {
                _logger.LogError($"Failed to scrape regions: {ex.Message}");
                throw;
            }
        }


        // 爬取城市景点数据
        public async Task ScrapeAttractionsForCityAsync(string cityUrl, long cityId)
        {
            try
            {
                var content = await MakeRequestWithRetryAsync(cityUrl);
                // 处理内容...
                var doc = new HtmlDocument();
                doc.LoadHtml(content);

                // 只选择第一个 <dl> 元素中的景点链接
                var attractions = doc.DocumentNode.SelectNodes("//div[@class='b_place_index' and @id='placebottomNav']//dl[1]//dd[@class='tag show']/a");
                if (attractions != null)
                {
                    foreach (var attraction in attractions)
                    {
                        var attractionUrl = attraction.GetAttributeValue("href", "");
                        await ScrapeAttractionDetailsAsync(attractionUrl, cityId);
                        await Task.Delay(5000); // 避免请求过快
                    }
                }
            }
            catch (HttpRequestException ex)
            {
                _logger.LogError($"Failed to scrape regions: {ex.Message}");
                throw;
            }
        }


        // 爬取景点详情
        public async Task ScrapeAttractionDetailsAsync(string attractionUrl, long cityId)
        {
            try
            {
                var content = await MakeRequestWithRetryAsync(attractionUrl);
                var doc = new HtmlDocument();
                doc.LoadHtml(content);

                var attraction = new Attraction
                {
                    RegionId = cityId,
                    Name = doc.DocumentNode.SelectSingleNode("//h1[@class='tit']")?.InnerText.Trim(),
                    Description = doc.DocumentNode.SelectSingleNode("//div[@class='e_db_content_box']")?.InnerText.Trim(),
                    Address = doc.DocumentNode.SelectSingleNode("//dt[text()='地址:']/following-sibling::dd/span")?.InnerText.Trim(),
                    Image = doc.DocumentNode.SelectSingleNode("//div[@class='originalbox']//img")?.GetAttributeValue("src", ""),
                    PriceRange = ExtractPriceRange(doc),
                    OpeningHours = doc.DocumentNode.SelectSingleNode("//dt[text()='开放时间:']/following-sibling::dd/span")?.InnerText.Trim(),
                    AttractionRating = ExtractRating(doc),
                    RecommendedVisitTime = doc.DocumentNode.SelectSingleNode("//div[@class='time']")?.InnerText.Replace("建议游览时间：", "").Trim(),
                    RecommendedVisitSeason = ExtractRecommendedVisitSeason(doc)
                };

                // 提取经纬度
                var mapBox = doc.DocumentNode.SelectSingleNode("//div[@class='mapbox']");
                if (mapBox != null)
                {
                    var latlng = mapBox.GetAttributeValue("latlng", "");
                    var coordinates = latlng.Split(',');
                    if (coordinates.Length == 2)
                    {
                        attraction.Latitude = decimal.Parse(coordinates[0]);
                        attraction.Longitude = decimal.Parse(coordinates[1]);
                    }
                }

                await _context.Attraction.AddAsync(attraction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error scraping attraction: {attractionUrl}");
            }
        }


        // 提取价格范围
        private string ExtractPriceRange(HtmlDocument doc)
        {
            var priceNode = doc.DocumentNode.SelectSingleNode("//div[@class='b_detail_section b_detail_ticket' and @data-key='ticket' and @id='mp']//div[@class='e_db_content_box e_db_content_dont_indent']/p");
            return priceNode?.InnerText.Trim() ?? "未知";
        }


        // 提取评分
        private decimal ExtractRating(HtmlDocument doc)
        {
            var ratingNode = doc.DocumentNode.SelectSingleNode("//div[@class='score']//em[@class='num']");
            if (decimal.TryParse(ratingNode?.InnerText.Trim(), out var rating))
            {
                return rating;
            }
            return 0;
        }

        // 提取旅游时节
        private string ExtractRecommendedVisitSeason(HtmlDocument doc)
        {
            var seasonNodes = doc.DocumentNode.SelectNodes("//div[@class='b_detail_section b_detail_travelseason' and @id='lysj']//div[@class='e_db_content_box e_db_content_dont_indent']/p");
            if (seasonNodes != null)
            {
                // 每段文字之间用换行符分隔
                return string.Join("\n", seasonNodes.Select(n => n.InnerText.Trim()));
            }
            return "未知";
        }
    }
}
